package org.jederik.studip.android.sync;

import org.jederik.android.provider.StudIP.*;
import org.jederik.studip.android.R;
import org.jederik.studip.android.conn.exceptions.*;
import org.jederik.studip.android.provider.StudipUriMatcher;

import android.app.IntentService;
import android.content.Intent;
import android.content.UriMatcher;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class SyncService extends IntentService {

	private static final String TAG = "SyncService";
	

	/** Intent-Kategorie um die Bestätigungs-Notification anzuzeigen */
	public static final String CATEGORY_AFFIRM = 
		"org.jederik.intent.category.AFFIRM";


	/** Key, unter dem im Extra-Bundle des Intents ein Messenger-Objekt für
	 * Callbacks registriert werden kann */
	public static final String CALLBACK_MESSENGER = "callback_messenger";


	// Mögliche Rückmeldungen an den Callback-Messenger:
	
	/** Rückmeldung: Synchronisation erfolgreich */
	public static final int SYNCHRONIZED = 1;
	
	/** Rückmeldung: Probleme beim Verbindungsaufbau */
	public static final int NO_CONNECTION = 2;
	
	/** Rückmeldung: Die Intent-Uri konnte nicht erkannt werden. */
	public static final int UNKNOWN_DATA = 4;
	
	/** Rückmeldung: Der Webservice hat den Status-Code 401 
	 * (Authentifizierung fehlgeschlagen) zurückgeliefert. */
	public static final int AUTHENTICATION_FAILED = 401;
	
	/** Rückmeldung: Der Webservice hat den Status-Code 403 
	 * (Resource nicht erlaubt) zurückgeliefert. */
	public static final int NOT_AUTHORIZED = 403;
	
	/** Rückmeldung: Der Webservice hat den Status-Code 404 
	 * (Resource nicht gefunden) zurückgeliefert. */
	public static final int NOT_FOUND = 404;
	
	/** Rückmeldung: Der Webservice hat den Status-Code 401 
	 * (Interner Server-Fehler) zurückgeliefert. */
	public static final int INTERNAL_SERVER_ERROR = 500;


	// Hilft bei der Erkennung der URIs:
	private UriMatcher mUriMatcher;
	
	// Stellt die Synchronisationsopertaionen bereit:
	private Processor mProcessor;

	// Handler der im MainThread läuft und somit Toasts anzeigen kann:
	private Handler mToaster;


	public SyncService() {
		super("sync_thread");
	}

	@Override
	public void onCreate() {
		super.onCreate();
		mUriMatcher = new StudipUriMatcher();
		mProcessor = Processor.getInstance(this);
		mToaster = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				String messageText = getString(msg.what);
				Log.d(TAG, msg.what + ": " + messageText);
				if (messageText != null) {
					Toast.makeText(SyncService.this, messageText, msg.arg1).show();
				}
			}
		};
	}
	
	@Override
	protected void onHandleIntent(Intent intent) {

		Log.d(TAG, "SYNC " + intent.getDataString());

		// Antwort an den Aufrufer:
		Message result = Message.obtain();

		try {

			String objectId;
			switch (mUriMatcher.match(intent.getData())) {
			
			case StudipUriMatcher._ALL:

				// Standartsynchronisation durchführen:
				mProcessor.synchronize();
				
				// Erfolgsmeldung an Callback anfügen:
				result.what = SYNCHRONIZED;

				break;

			case StudipUriMatcher.FEED:

				// Feed synchronisieren:
				mProcessor.synchronizeFeed();
				
				// Erfolgsmeldung an Callback anfügen:
				result.what = SYNCHRONIZED;
				
				break;

			case StudipUriMatcher.SCHEDULES:

				// Stundenplan mit Kalender synchronisieren:
				mProcessor.synchronizeSchedule();
				
				// Erfolgsmeldung an Callback anfügen:
				result.what = SYNCHRONIZED;

				break;

			case StudipUriMatcher.MYCOURSES:

				// "Meine Veranstaltungen" synchronisieren:
				mProcessor.synchronizeMycourses();
				
				// Erfolgsmeldung an Callback anfügen:
				result.what = SYNCHRONIZED;

				break;

			case StudipUriMatcher.NEWS_ID:
				
				// Objekt-ID aus URI auslesen:
				objectId = intent.getData().getLastPathSegment();
				mProcessor.synchronizeResource(intent.getData(), News.DB_TABLE_NAME, "/news/" + objectId, objectId);
				break;

			case StudipUriMatcher.MESSAGES_ID:

				// Objekt-ID aus URI auslesen:
				objectId = intent.getData().getLastPathSegment();
				
				// Resource synchronisieren:
				mProcessor.synchronizeResource(intent.getData(), Messages.DB_TABLE_NAME, "/messages/" + objectId, objectId);
				break;

			case StudipUriMatcher.SCHEDULES_ID:

				// Objekt-ID aus URI auslesen:
				objectId = intent.getData().getLastPathSegment();
				
				// Resource synchronisieren:
				mProcessor.synchronizeResource(intent.getData(), Schedules.DB_TABLE_NAME, "/schedules/" + objectId, objectId);

				break;

			case StudipUriMatcher.COURSES_ID_DETAILS:

				// Objekt-ID aus URI auslesen:
				objectId = intent.getData().getPathSegments().get(1);
				
				// Resource synchronisieren:
				mProcessor.synchronizeResource(intent.getData(), CourseDetails.DB_TABLE_NAME, "/courses/" + objectId + "/details", objectId);

				break;

			case StudipUriMatcher.FEED_ID:
				
				// Objekt-ID aus URI auslesen:
				objectId = intent.getData().getLastPathSegment();
				
				// Resource synchronisieren:
				mProcessor.synchronizeObjectVisit(objectId);

				break;
				
			case StudipUriMatcher.MESSAGES_SENT_ID:

				// Objekt-ID aus URI auslesen:
				long id = Long.parseLong(intent.getData().getLastPathSegment());

				// Resource synchronisieren:
				mProcessor.synchronizeSentMessage(id);

				// Bestätigungstoast einblenden:
				Message.obtain(
						mToaster, 
						R.string.send_message_complete, 
						Toast.LENGTH_SHORT, 
						0
				).sendToTarget();
				
				break;

			default:
				result.what = UNKNOWN_DATA;
			}
			
		} catch (ConnectionErrorException e) {
			e.printStackTrace();
			Message.obtain(
					mToaster, 
					R.string.service_not_available, 
					Toast.LENGTH_LONG, 
					0
			).sendToTarget();
			result.what = NO_CONNECTION;
		} catch (AuthenticationException e) {
			e.printStackTrace();
			Message.obtain(
					mToaster, 
					R.string.authentication_error, 
					Toast.LENGTH_LONG, 
					0
			).sendToTarget();
			result.what = AUTHENTICATION_FAILED;
		} catch (AuthorizationException e) {
			e.printStackTrace();
			Message.obtain(
					mToaster, 
					R.string.not_authorized, 
					Toast.LENGTH_LONG, 
					0
			).sendToTarget();
			result.what = NOT_AUTHORIZED;
		} catch (NotFoundException e) {
			e.printStackTrace();
			Message.obtain(
					mToaster, 
					R.string.resource_not_found, 
					Toast.LENGTH_LONG, 
					0
			).sendToTarget();
			result.what = NOT_FOUND;
		} catch (InternalServerErrorException e) {
			e.printStackTrace();
			Message.obtain(
					mToaster, 
					R.string.internal_server_error, 
					Toast.LENGTH_LONG, 
					0
			).sendToTarget();
			result.what = INTERNAL_SERVER_ERROR;
		}

		try {
			// Falls verlangt, Antwort an Aufrufer senden:
			Messenger callbackMessenger = (Messenger) intent.getParcelableExtra(CALLBACK_MESSENGER);
			if (callbackMessenger==null) throw new RemoteException();
			callbackMessenger.send(result);
		} catch (RemoteException e) {
			Log.i(TAG, "No Callback Messenger defined.");
		}
	}
}
